package com.jiazixuan.stock.service.impl;

import com.alibaba.excel.EasyExcel;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.jiazixuan.stock.mapper.*;
import com.jiazixuan.stock.pojo.domain.*;
import com.jiazixuan.stock.pojo.vo.StockInfoConfig;
import com.jiazixuan.stock.service.StockService;
import com.jiazixuan.stock.utils.DateTimeUtil;
import com.jiazixuan.stock.vo.resp.PageResult;
import com.jiazixuan.stock.vo.resp.R;
import com.jiazixuan.stock.vo.resp.ResponseCode;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

@Service("stockServiceImpl")
@Slf4j
public class StockServiceImpl implements StockService {

    @Autowired
    private StockInfoConfig stockInfoConfig;

    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;

    @Autowired
    private Cache<String,Object> caffeineCache;
    /**
     * 获取国内大盘的实时数据
     * @return
     */
    @Override
    public R<List<InnerMarketDomain>> innerIndexAll() {
        /**
         * 默认从本地缓存加载数据，如果不存在则从数据库中加载并同步到本地缓存
         * 在开盘周期内，本地缓存默认有效期为1分钟
         */
        R<List<InnerMarketDomain>> result = (R<List<InnerMarketDomain>>) caffeineCache.get("innerMarketKey", key->{
            //获取最近股票交易时间
            Date lastDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
            lastDate=DateTime.parse("2021-12-28 09:31:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
            //获取国内大盘编码
            List<String> inners = stockInfoConfig.getInner();
            //将获取的Java Date传入接口
            List<InnerMarketDomain> list = stockMarketIndexInfoMapper.getMarketInfo(lastDate,inners);
            return R.ok(list);
        });
        return result;
    }

    @Autowired
    private StockBlockRtInfoMapper stockBlockRtInfoMapper;
    /**
     *需求说明: 沪深两市板块分时行情数据查询，以交易时间和交易总金额降序查询，取前10条数据
     * @return
     */
    @Override
    public R<List<StockBlockDomain>> sectorAll() {
        //获取最新股票交易时间
        Date lastDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        lastDate=DateTime.parse("2021-12-21 14:30:00",DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //调用mapper接口
        List<StockBlockDomain> infos =  stockBlockRtInfoMapper.sectorAllLimit(lastDate);
        //组装数据
        if (CollectionUtils.isEmpty(infos)) {
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        return R.ok(infos);
    }


    @Autowired
    private StockRtInfoMapper stockRtInfoMapper;
    /**
     * 涨幅榜分页查询功能
     * 分页查询最新股票交易时间点下沪深两市个股行情数据，并根据涨幅降序排序展示
     */
    @Override
    public R<PageResult<StockUpdownDomain>> getStockInfoByPage(Integer page,Integer pageSize) {
        //获取最新股票交易时间
        Date lastDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        lastDate=DateTime.parse("2021-12-30 09:42:00",DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //设置分页参数
        PageHelper.startPage(page,pageSize);
        //调用mapper接口
        List<StockUpdownDomain> pageData = stockRtInfoMapper.getStockInfoByTime(lastDate);
        //组装数据
        PageInfo<StockUpdownDomain> pageInfo = new PageInfo<>(pageData);
        PageResult<StockUpdownDomain> pageResult = new PageResult<>(pageInfo);
        return R.ok(pageResult);
    }

    /**
     * 统计沪深两市个股最新交易数据，并按涨幅降序排序查询前4条数据
     * @return
     */
    @Override
    public R<List<StockUpdownDomain>> getStockInfoAnd4() {
        //获取最新时间
        Date lastDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        lastDate=DateTime.parse("2021-12-30 09:42:00",DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //调用mapper接口
        List<StockUpdownDomain> pageDataAnd4 = stockRtInfoMapper.getStockInfoByTimeAnd4(lastDate);
        //组装数据
        return R.ok(pageDataAnd4);
    }

    /**
     * 统计最新股票交易日内每分钟的涨跌停的股票数量
     * @return
     */
    @Override
    public R<Map<String, List>> getStockUpDownCount() {
        //获取最新股票交易时间点（截止时间）
        DateTime curDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        curDateTime = DateTime.parse("2022-01-06 14:25:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date endDate = curDateTime.toDate();
        //获取开始时间
        Date startDate = DateTimeUtil.getOpenDate(curDateTime).toDate();
        //查询涨跌停数据
        //涨停数据
        List<Map> upCounts = stockRtInfoMapper.getStockUpdownCount(startDate,endDate,1);
        //跌停数据
        List<Map> dwCounts = stockRtInfoMapper.getStockUpdownCount(startDate,endDate,0);
        //组装数据
        HashMap<String, List> mapInfo = new HashMap<>();
        mapInfo.put("upList",upCounts);
        mapInfo.put("downList",dwCounts);
        return R.ok(mapInfo);
    }

    @Override
    public void exportStockUpDownInfo(Integer page, Integer pageSize, HttpServletResponse response) {
        //获取要导出的数据
        R<PageResult<StockUpdownDomain>> r = this.getStockInfoByPage(page, pageSize);
        List<StockUpdownDomain> rows = r.getData().getRows();
        //将数据导出到excel表中
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        try {
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系（默认名称）
            String fileName = URLEncoder.encode("股票信息表", "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
            EasyExcel.write(response.getOutputStream(), StockUpdownDomain.class).sheet("股票涨幅信息").doWrite(rows);
        } catch (IOException e) {
            log.error("当前页码：{},每页大小：{},当前时间：{},异常信息：{}",page,pageSize,DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),e.getMessage());
            //通知前端异常，稍后重试
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            R<Object> error = R.error(ResponseCode.ERROR);
            try {
                //返回给浏览器错误信息
                String jsonData = new ObjectMapper().writeValueAsString(error);
                response.getWriter().write(jsonData);
            } catch (IOException ioException) {
                log.error("当前页码：{},每页大小：{},当前时间：{},异常信息：{}",page,pageSize,DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),e.getMessage());
            }
        }
    }

    /**
     * 统计大盘股票T日和T-1日每分钟交易量的统计（成交量为沪市和深市成交量之和）
     * @return
     */
    @Override
    public R<Map<String, List>> getComparedStockTradeAmt() {
        //获取T日日期
        DateTime tEndDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //mock time
        tEndDateTime=DateTime.parse("2022-01-03 14:40:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date tEndDate = tEndDateTime.toDate();
        //获取T日开盘时间
        Date tStartDate = DateTimeUtil.getOpenDate(tEndDateTime).toDate();

        //获取T-1日日期
        DateTime preTEndDateTime = DateTimeUtil.getPreDateTime(tEndDateTime);
        //mock time
        preTEndDateTime=DateTime.parse("2022-01-02 14:40:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date preTEndDate = preTEndDateTime.toDate();
        //T-1日开盘时间
        Date preTStartDate = DateTimeUtil.getOpenDate(preTEndDateTime).toDate();

        //调用mapper接口
        List<Map> tDate = stockMarketIndexInfoMapper.getSumAmtInfo(tStartDate,tEndDate,stockInfoConfig.getInner());
        List<Map> tPreDate = stockMarketIndexInfoMapper.getSumAmtInfo(preTStartDate,preTEndDate,stockInfoConfig.getInner());

        //组装数据
        HashMap<String, List> info = new HashMap<>();
        info.put("amtList",tDate);
        info.put("yesAmtList",tPreDate);
        //返回数据
        return R.ok(info);
    }

    /**
     * 个股涨跌
     * @return
     */
    @Override
    public R<Map<String, Object>> getUpDown() {
        //获取最新交易时间
        Date curTime = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        //mock time
        curTime=DateTime.parse("2022-01-06 09:55:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //调用mapper接口
        List<Map> infos = stockRtInfoMapper.getUpDown(curTime);
        //获取有序的涨幅区间标题集合
        List<String> upDownRange = stockInfoConfig.getUpDownRange();
        //将顺序的涨幅区间内的每个元素转化为Map对象即可
//        List<Map> allInfos = new ArrayList<>();
//        for (String title : upDownRange) {
//            Map tmp=null;
//            /**
//             * "count": 17,
//             * "title": "-3~0%"   key是title  value是"-3~0%"，该代码中的title指的就是"-3~0%"，而非key
//             */
//            for (Map info : infos) {
//                if (info.containsValue(title)) {
//                    tmp=info;
//                    break;
//                }
//            }
//            //查询到的数据中没有某个百分区间
//            if (tmp==null) {
//                tmp=new HashMap();
//                tmp.put("count",0);
//                tmp.put("title",title);
//            }
//                allInfos.add(tmp);
//        }
        //方式2：使用lambda表达式指定
        List<Map> allInfos = upDownRange.stream().map(title -> {
            Optional<Map> result = infos.stream().filter(map -> map.containsValue(title)).findFirst();
            if (result.isPresent()) {
                return result.get();
            } else {
                HashMap<String, Object> tmp = new HashMap<>();
                tmp.put("count", 0);
                tmp.put("title", title);
                return tmp;
            }
        }).collect(Collectors.toList());
        //组装数据
        HashMap<String, Object> date = new HashMap<>();
        //获取指定日期格式的字符串
        String curDateStr = new DateTime(curTime).toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        date.put("time",curDateStr);
        date.put("infos",allInfos);
        return R.ok(date);
    }

    /**
     * 功能描述：查询单个个股的分时行情数据，也就是统计指定股票T日每分钟的交易数据
     *          如果当前日期不在有效时间内，则以最近的一个股票交易时间作为查询时间点
     * @param code 股票编码
     * @return
     */
    @Override
    public R<List<Stock4MinuteDomain>> stockScreenTimeSharing(String code) {
        //获取最新股票交易时间点
        DateTime endDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        // mockdata
        endDateTime=DateTime.parse("2021-12-30 14:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date endDate = endDateTime.toDate();
        Date openDate = DateTimeUtil.getOpenDate(endDateTime).toDate();
        //调用mapper接口
        List<Stock4MinuteDomain> data = stockRtInfoMapper.getstockScreenTimeSharing(openDate,endDate,code);
        return R.ok(data);
    }

    @Override
    public R<List<Stock4EvrDayDomain>> stockScreenTimeDay(String code) {
        //获取日k线数据最后一个时间日期
        DateTime endDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //mock time
        endDateTime=DateTime.parse("2022-06-06 14:25:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date endDate = endDateTime.toDate();
        //获取日k线数据第一个时间日期
        DateTime startDateTime = endDateTime.minusMonths(3);
        startDateTime=DateTime.parse("2022-01-01 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date startDate = startDateTime.toDate();
        // 第一步：查询时间范围内“每天最后一个交易时间点”，只定位日K需要保留的记录
        List<Date> lastTimesPerDay = stockRtInfoMapper.listMaxCurTimePerDay(startDate, endDate, code);
        if (CollectionUtils.isEmpty(lastTimesPerDay)) {
            return R.ok(Collections.emptyList());
        }
        // 第二步：根据这些时间点一次性取出明细，得到日K需要的价格/交易量信息
        List<Stock4EvrDayDomain> data = stockRtInfoMapper.listStockInfoByCurTimes(code, lastTimesPerDay);
        //返回数据
        return R.ok(data);
    }

    @Autowired
    private StockOuterMarketIndexInfoMapper stockOuterMarketIndexInfoMapper;
    @Override
    public R<List<OuterMarketDomain>> outerMarket() {
        //获取时间
        DateTime dateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //mock time
        dateTime = DateTime.parse("2021-12-01 10:57:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date date = dateTime.toDate();
        //调用mapper接口
        List<OuterMarketDomain> info = stockOuterMarketIndexInfoMapper.outerMarket(date);
        //返回数据
        return R.ok(info);
    }

    @Override
    public R<List<Map>> searchStock(String searchStr) {
        //直接调用mapper接口
        List info = stockOuterMarketIndexInfoMapper.searchStock(searchStr);

     //   HashMap<String, Object> map = new HashMap<>();
        //组装数据
        return R.ok(info);
    }

    /**
     * 周K
     */
    @Override
    public R<List<Stock4EvrWeekDomain>> getWeekStock(String code) {
        //时间
        DateTime startDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //mock time
        startDateTime = DateTime.parse("2021-12-30 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date startDate = startDateTime.toDate();
        DateTime endDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        endDateTime = DateTime.parse("2022-06-06 14:25:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date endDate = endDateTime.toDate();
        // 第一步：按周聚合，得到均价、最高/最低价以及周内最小/最大交易时间
        List<Stock4EvrWeekDomain> weekStats = stockRtInfoMapper.listWeekStock(startDate, endDate, code);
        if (CollectionUtils.isEmpty(weekStats)) {
            return R.ok(Collections.emptyList());
        }
        // 第二步：收集所有周的最小/最大交易时间点，用于后续一次性查开盘/收盘价
        List<Date> probeTimes = weekStats.stream()
                .flatMap(week -> Arrays.asList(week.getMiTime(), week.getMxTime()).stream())
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
        Map<Date, Stock4EvrDayDomain> pricePointMap = Collections.emptyMap();
        if (!CollectionUtils.isEmpty(probeTimes)) {
            // 第三步：复用日K明细查询方法，将所有需要的时间点一次查出，避免 N 次数据库请求
            List<Stock4EvrDayDomain> pricePoints = stockRtInfoMapper.listStockInfoByCurTimes(code, probeTimes);
            pricePointMap = pricePoints.stream()
                    .collect(Collectors.toMap(Stock4EvrDayDomain::getDate, point -> point, (a, b) -> a));
        }
        // 第四步：把查询到的开/收盘价回填到每周的聚合结果上
        for (Stock4EvrWeekDomain weekStat : weekStats) {
            Stock4EvrDayDomain openPoint = pricePointMap.get(weekStat.getMiTime());
            if (openPoint != null) {
                weekStat.setOpenPrice(openPoint.getOpenPrice());
            }
            Stock4EvrDayDomain closePoint = pricePointMap.get(weekStat.getMxTime());
            if (closePoint != null) {
                weekStat.setClosePrice(closePoint.getClosePrice());
            }
        }
        return R.ok(weekStats);
    }

    @Autowired
    private StockBusinessMapper  stockBusinessMapper;
    /**
     * 个股主营业务
     */
    @Override
    public R<StockBusinessInfomation> getInfomation(String code) {
        //调用mapper接口
        StockBusinessInfomation info = stockBusinessMapper.getInfomation(code);
        //返回数据
        return R.ok(info);
    }

    /**
     * 获取个股最新分时行情数据
     */
    @Override
    public R<StockNewMinuteDomain> getStockNew(String code) {
        //时间
        DateTime dateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        dateTime = DateTime.parse("2021-12-30 09:32:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date date = dateTime.toDate();
        //调用mapper接口
        StockNewMinuteDomain info = stockRtInfoMapper.getStockNew(date,code);
        return R.ok(info);
    }

    /**
     * 查询最新交易流水
     */
    @Override
    public R<List<StockTransactionFlow>> getStockTransactionFlow(String code) {
        //调用mapper接口
        List<StockTransactionFlow> infos = stockRtInfoMapper.getStockTransactionFlow(code);
        return R.ok(infos);

    }
}






















